home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / IFC_112 / netscape / util / Archive.java next >
Encoding:
Text File  |  1999-05-28  |  13.0 KB  |  427 lines  |  [TEXT/CWIE]

  1. // Archive.java
  2. // By Ned Etcode
  3. // Copyright 1995, 1996, 1997 Netscape Communications Corp.  All rights reserved.
  4.  
  5. package netscape.util;
  6.  
  7. import java.io.*;
  8.  
  9. /** Object subclass used by the Archiver and Unarchiver to store the
  10.   * encoded state of Codable objects. Archives contain a collection of
  11.   * ClassTables and a mapping of object identifiers to a row in a specific
  12.   * ClassTable. Each ClassTable row corresponds to an encoded object.
  13.   * References between objects are encoded as opaque integers (identifiers).
  14.   * The archive also maintains a set of root identifiers from which all
  15.   * objects in the archive can be reached. Most programs will not need to
  16.   * deal directly with the Archive class, but instead will work with Archivers
  17.   * and Unarchivers.
  18.   *
  19.   * @see ClassTable
  20.   * @see Archiver
  21.   * @see Unarchiver
  22.   * @note 1.0 Made the binary archive magic number public
  23.   */
  24. public class Archive {
  25.     /** @private */
  26.     public static final int ARCHIVE_MAGIC = 0x6E656421;
  27.     static final int ARCHIVE_VERSION = 1;
  28.  
  29.     // This can be static for now since it is hidden and immutable.
  30.     static Hashtable externalCoders;
  31.  
  32.     int version;
  33.  
  34.     int rootCount;
  35.     int roots[];
  36.  
  37.     int idCount;
  38.     int rowForId[];
  39.     ClassTable tableForId[];
  40.  
  41.     Hashtable classTables;
  42.  
  43.     /** When set to <b>true</b>, the Archive will print messages when fields
  44.       * are accessed out of order.  Out of order access reduces archiving
  45.       * performance.
  46.       * @private
  47.       */
  48.     public boolean performanceDebug = false;
  49.  
  50.     /** Constructs a new, empty Archive.
  51.       */
  52.     public Archive() {
  53.         super();
  54.  
  55.         rootCount = 0;
  56.         roots = new int[4];
  57.  
  58.         // By convention id = 0 maps to the null object.  It has no table
  59.         // or row, but is implicitly in every archive.
  60.  
  61.         idCount = 1;
  62.         rowForId = new int[4];
  63.         tableForId = new ClassTable[4];
  64.  
  65.         classTables = new Hashtable();
  66.     }
  67.  
  68.     private static int[] growIntArray(int array[]) {
  69.         int newArray[];
  70.  
  71.         newArray = new int[array.length * 2];
  72.         System.arraycopy(array, 0, newArray, 0, array.length);
  73.  
  74.         return newArray;
  75.     }
  76.  
  77.     private static ClassTable[] growTableArray(ClassTable array[]) {
  78.         ClassTable newArray[];
  79.  
  80.         newArray = new ClassTable[array.length * 2];
  81.         System.arraycopy(array, 0, newArray, 0, array.length);
  82.  
  83.         return newArray;
  84.     }
  85.  
  86.     /** Adds an identifier to the array of root identifiers. The identifier
  87.       * must be created by calling <b>newIdentifier()</b> on a ClassTable.
  88.       * @see ClassTable#newIdentifier
  89.       */
  90.     public void addRootIdentifier(int id) {
  91.         if (rootCount >= roots.length)
  92.             roots = growIntArray(roots);
  93.  
  94.         roots[rootCount] = id;
  95.         rootCount++;
  96.     }
  97.  
  98.     /** Removes an identifier from the array of root identifiers. Returns
  99.       * <b>true</b> if the identifier was in the array, <b>false</b> if it
  100.       * was not.
  101.       * @see ClassTable#addRootIdentifier
  102.       */
  103.     public boolean removeRootIdentifier(int id) {
  104.         int i;
  105.         boolean removed = false;
  106.  
  107.         for (i = 0; i < rootCount; i++) {
  108.             if (roots[i] == id) {
  109.                 removed = true;
  110.                 rootCount--;
  111.                 break;
  112.             }
  113.         }
  114.  
  115.         for (; i < rootCount; i++)
  116.             roots[i] = roots[i + 1];
  117.  
  118.         return removed;
  119.     }
  120.  
  121.     /** Returns a copy of the root identifier array.
  122.       */
  123.     public int[] rootIdentifiers() {
  124.         int rootsCopy[];
  125.  
  126.         rootsCopy = new int[rootCount];
  127.         System.arraycopy(roots, 0, rootsCopy, 0, rootCount);
  128.  
  129.         return rootsCopy;
  130.     }
  131.  
  132.     /** Returns the ClassTable for a given class name.
  133.       */
  134.     public ClassTable classTableForName(String className) {
  135.         return (ClassTable)classTables.get(className);
  136.     }
  137.  
  138.     /** Adds a new ClassTable to the archive. This should be called after
  139.       * creating a new ClassTable.
  140.       * @see ClassTable
  141.       */
  142.     public void addClassTable(ClassTable table) {
  143.         classTables.put(table.className(), table);
  144.     }
  145.  
  146.     /** Returns the ClassTable for a given object identifier in the archive.
  147.       * @see ClassTable
  148.       */
  149.     public ClassTable classTableForIdentifier(int id) {
  150.         if (id >= idCount)
  151.             throw new ArrayIndexOutOfBoundsException(id);
  152.  
  153.         return tableForId[id];
  154.     }
  155.  
  156.     /** Returns the row for a given object identifier in the archive.
  157.       * @see ClassTable
  158.       */
  159.     public int rowForIdentifier(int id) {
  160.         if (id >= idCount)
  161.             throw new ArrayIndexOutOfBoundsException(id);
  162.  
  163.         return rowForId[id];
  164.     }
  165.  
  166.     /** Primitive method for mapping a ClassTable and row to a new object
  167.       * identifier. Most programs will simply call
  168.       * <b>ClassTable.newIdentifier()</b> to get a new object identifier.
  169.       * @see ClassTable#newIdentifier
  170.       */
  171.     public int mapIdentifier(ClassTable table, int row) {
  172.         int id;
  173.  
  174.         if (idCount >= rowForId.length) {
  175.             rowForId = growIntArray(rowForId);
  176.             tableForId = growTableArray(tableForId);
  177.         }
  178.  
  179.         id = idCount;
  180.         rowForId[id] = row;
  181.         tableForId[id] = table;
  182.         idCount++;
  183.  
  184.         return id;
  185.     }
  186.  
  187.     /** Returns the number of object identifiers in the archive. All
  188.       * identifiers will be between 0 and (identifierCount() - 1), inclusive.
  189.       */
  190.     public int identifierCount() {
  191.         return idCount;
  192.     }
  193.  
  194.     /** This is a hook which allows objects to be registered that know how
  195.       * to codify objects which don't themselves implement Codable. If this
  196.       * works well, this should be made public in the next release.
  197.       */
  198.     static synchronized void setupExternalCoders() {
  199.         PrimitiveCoder coder;
  200.  
  201.         if (externalCoders != null)
  202.             return;
  203.  
  204.         externalCoders = new Hashtable(24);
  205.  
  206.         coder = new PrimitiveCoder(Codable.BOOLEAN_TYPE);
  207.         externalCoders.put(coder.className(), coder);
  208.         coder = new PrimitiveCoder(Codable.BOOLEAN_ARRAY_TYPE);
  209.         externalCoders.put(coder.className(), coder);
  210.         coder = new PrimitiveCoder(Codable.CHAR_TYPE);
  211.         externalCoders.put(coder.className(), coder);
  212.         coder = new PrimitiveCoder(Codable.CHAR_ARRAY_TYPE);
  213.         externalCoders.put(coder.className(), coder);
  214.         coder = new PrimitiveCoder(Codable.BYTE_TYPE);
  215.         externalCoders.put(coder.className(), coder);
  216.         coder = new PrimitiveCoder(Codable.BYTE_ARRAY_TYPE);
  217.         externalCoders.put(coder.className(), coder);
  218.         coder = new PrimitiveCoder(Codable.SHORT_TYPE);
  219.         externalCoders.put(coder.className(), coder);
  220.         coder = new PrimitiveCoder(Codable.SHORT_ARRAY_TYPE);
  221.         externalCoders.put(coder.className(), coder);
  222.         coder = new PrimitiveCoder(Codable.INT_TYPE);
  223.         externalCoders.put(coder.className(), coder);
  224.         coder = new PrimitiveCoder(Codable.INT_ARRAY_TYPE);
  225.         externalCoders.put(coder.className(), coder);
  226.         coder = new PrimitiveCoder(Codable.LONG_TYPE);
  227.         externalCoders.put(coder.className(), coder);
  228.         coder = new PrimitiveCoder(Codable.LONG_ARRAY_TYPE);
  229.         externalCoders.put(coder.className(), coder);
  230.         coder = new PrimitiveCoder(Codable.FLOAT_TYPE);
  231.         externalCoders.put(coder.className(), coder);
  232.         coder = new PrimitiveCoder(Codable.FLOAT_ARRAY_TYPE);
  233.         externalCoders.put(coder.className(), coder);
  234.         coder = new PrimitiveCoder(Codable.DOUBLE_TYPE);
  235.         externalCoders.put(coder.className(), coder);
  236.         coder = new PrimitiveCoder(Codable.DOUBLE_ARRAY_TYPE);
  237.         externalCoders.put(coder.className(), coder);
  238.         coder = new PrimitiveCoder(Codable.STRING_TYPE);
  239.         externalCoders.put(coder.className(), coder);
  240.         coder = new PrimitiveCoder(Codable.STRING_ARRAY_TYPE);
  241.         externalCoders.put(coder.className(), coder);
  242.     }
  243.  
  244.     ExternalCoder externalCoderForName(String className) {
  245.         if (externalCoders == null) {
  246.             setupExternalCoders();
  247.         }
  248.  
  249.         return (ExternalCoder)externalCoders.get(className);
  250.     }
  251.  
  252.     /** Reads a binary serialization of the Archive's contents from
  253.       * <b>inputStream</b>.
  254.       * @see Unarchiver#readObject
  255.       */
  256.     public void read(InputStream inputStream) throws IOException {
  257.         int i, count, magic;
  258.         ClassTable table, tables[];
  259.         CompactInputStream in;
  260.  
  261.         if (inputStream instanceof CompactInputStream)
  262.             in = (CompactInputStream)inputStream;
  263.         else
  264.             in = new CompactInputStream(inputStream);
  265.  
  266.         // Read in the magic number and archive version.
  267.  
  268.         magic = in.readInt();
  269.         if (magic != ARCHIVE_MAGIC)
  270.             throw new IOException("Bad magic number " + magic);
  271.  
  272.         version = in.readInt();
  273.         if (version != ARCHIVE_VERSION)
  274.             throw new IOException("Unknown archiveVersion " + version);
  275.  
  276.         // Read in all the ClassTables.
  277.  
  278.         count = in.readCompactInt();
  279.         tables = new ClassTable[count];
  280.  
  281.         for (i = 0; i < count; i++) {
  282.             table = new ClassTable(this);
  283.             tables[i] = table;
  284.             table.readInfo(in);
  285.             addClassTable(table);
  286.         }
  287.  
  288.         for (i = 0; i < count; i++) {
  289.             table = tables[i];
  290.             table.readData(in);
  291.         }
  292.  
  293.         // Read in the root ids.
  294.  
  295.         count = in.readCompactInt();
  296.         roots = new int[count];
  297.         rootCount = count;
  298.  
  299.         for (i = 0; i < count; i++) {
  300.             roots[i] = in.readCompactInt();
  301.         }
  302.  
  303.         // Read in the id mappings.  This must happen after reading the
  304.         // ClassTables or the tables array will not be set properly.
  305.  
  306.         count = in.readCompactInt();
  307.         rowForId = new int[count];
  308.         tableForId = new ClassTable[count];
  309.         idCount = count;
  310.  
  311.         for (i = 1; i < count; i++) {
  312.             rowForId[i] = in.readCompactInt();
  313.             tableForId[i] = tables[in.readCompactInt()];
  314.         }
  315.  
  316.         // Leave room for expansion.
  317.  
  318.         count = in.readCompactInt();
  319.         if (count > 0)
  320.             in.skipBytes(count);
  321.     }
  322.  
  323.     /** Writes a binary serialization of the Archive's contents to
  324.       * <b>outputStream</b>.
  325.       * @see Archiver#writeObject
  326.       */
  327.     public void write(OutputStream outputStream) throws IOException {
  328.         int i, count;
  329.         Object tables[];
  330.         CompactOutputStream out;
  331.         ClassTable table;
  332.  
  333.         if (outputStream instanceof CompactOutputStream)
  334.             out = (CompactOutputStream)outputStream;
  335.         else
  336.             out = new CompactOutputStream(outputStream);
  337.  
  338.         // Write out the magic number and archive version.  To be nice to
  339.         // others, we won't write these out in compact form.
  340.  
  341.         out.writeInt(ARCHIVE_MAGIC);
  342.         out.writeInt(ARCHIVE_VERSION);
  343.  
  344.         // Write out all the ClassTables.  We need to map each ClassTable to
  345.         // an int which we cache in ClassTable.tableId.  This id is good only
  346.         // for this write.
  347.  
  348.         tables = classTables.elementsArray();
  349.         if (tables == null)
  350.             count = 0;
  351.         else
  352.             count = tables.length;
  353.  
  354.         out.writeCompactInt(count);
  355.  
  356.         // Write out all the meta-data first.
  357.  
  358.         for (i = 0; i < count; i++) {
  359.             table = (ClassTable)tables[i];
  360.             table.tableId = i;
  361.             table.writeInfo(out);
  362.         }
  363.  
  364.         // Write out all the instance data.
  365.  
  366.         for (i = 0; i < count; i++) {
  367.             table = (ClassTable)tables[i];
  368.             table.writeData(out);
  369.         }
  370.  
  371.         // Write out the root ids.
  372.  
  373.         count = rootCount;
  374.         out.writeCompactInt(count);
  375.         for (i = 0; i < count; i++)
  376.             out.writeCompactInt(roots[i]);
  377.  
  378.         // Write out the id mappings.  This must happen after writing out the
  379.         // ClassTables or the tableId will not be set properly.
  380.  
  381.         count = idCount;
  382.         out.writeCompactInt(count);
  383.         for (i = 1; i < count; i++) {
  384.             out.writeCompactInt(rowForId[i]);
  385.             table = tableForId[i];
  386.             if (table == null)
  387.                 out.writeCompactInt(0);
  388.             else
  389.                 out.writeCompactInt(table.tableId);
  390.         }
  391.  
  392.         // Leave room for expansion.
  393.  
  394.         out.writeCompactInt(0);
  395.  
  396.         out.flush();
  397.     }
  398.  
  399.     /** Reads an ASCII serialization of the Archive's contents from
  400.       * <b>inputStream</b>.
  401.       * @see Serializer
  402.       * @see Deserializer
  403.       */
  404.     public void readASCII(InputStream inputStream) throws CodingException,
  405.         DeserializationException, IOException {
  406.         ASCIIArchiveLoader loader;
  407.  
  408.         loader = new ASCIIArchiveLoader(this);
  409.         loader.readASCII(inputStream);
  410.     }
  411.  
  412.     /** Writes an ASCII serialization of the Archive's contents to
  413.       * <b>outputStream</b>.  If <b>formatted</b> is <b>true</b>, the Archive
  414.       * formats the output for easy reading.  If <b>false</b>, the output will
  415.       * be as compact as possible.
  416.       * @see Serializer
  417.       * @see Deserializer
  418.       */
  419.     public void writeASCII(OutputStream outputStream, boolean formatted)
  420.         throws CodingException, IOException {
  421.         ASCIIArchiveLoader loader;
  422.  
  423.         loader = new ASCIIArchiveLoader(this);
  424.         loader.writeASCII(outputStream, formatted);
  425.     }
  426. }
  427.